#include "endpoint.h"
using namespace boost;

int client_end_point()
{
	// 步骤 1. 假设客户端应用程序已经获取了
	// IP地址和协议端口号
	std::string raw_ip_address = "127.0.0.1";
	unsigned short port_num = 3333;

	// 用于存储解析原始IP地址时可能发生的错误信息
	boost::system::error_code ec;
	// 步骤 2. 使用与IP协议版本无关的地址表示
	asio::ip::address ip_address =
		asio::ip::address::from_string(raw_ip_address, ec);

	if (ec.value() != 0)
	{
		// 提供的IP地址无效，终止执行
		std::cout
			<< "解析IP地址失败。错误代码 = "
			<< ec.value() << "。消息：" << ec.message();
		return ec.value();
	}

	// 步骤 3.
	asio::ip::tcp::endpoint ep(ip_address, port_num);

	// 步骤 4. 端点已准备就绪，可用于指定
	// 客户端想要通信的网络中的特定服务器

	return 0;
}

int server_end_point()
{
	// 步骤 1. 这里我们假设服务器应用程序已经
	// 获取了协议端口号
	unsigned short port_num = 3333;

	// 步骤 2. 创建asio::ip::address类的特殊对象
	// 用于指定主机上所有可用的IP地址
	// 注意这里我们假设服务器使用IPv6协议
	asio::ip::address ip_address = asio::ip::address_v6::any();

	// 步骤 3.
	asio::ip::tcp::endpoint ep(ip_address, port_num);

	// 步骤 4. 端点已创建，可用于指定服务器应用程序
	// 想要监听传入连接的IP地址和端口号

	return 0;
}

int create_tcp_socket()
{
	// 步骤 1. socket构造函数需要一个'io_service'类的实例
	asio::io_context ios;

	// 步骤 2. 创建一个'tcp'类对象，表示
	// 使用IPv4作为底层协议的TCP协议
	asio::ip::tcp protocol = asio::ip::tcp::v4();

	// 步骤 3. 实例化一个活动的TCP socket对象
	asio::ip::tcp::socket sock(ios);

	// 用于存储打开socket时可能发生的错误信息
	boost::system::error_code ec;

	// 步骤 4. 打开socket
	sock.open(protocol, ec);

	if (ec.value() != 0)
	{
		// socket打开失败
		std::cout
			<< "打开socket失败！错误代码 = "
			<< ec.value() << "。消息：" << ec.message();
		return ec.value();
	}

	return 0;
}

int create_acceptor_socket()
{
	// 步骤 1. socket构造函数需要一个'io_service'类的实例
	asio::io_context ios;

	// 步骤 2. 创建一个'tcp'类对象，表示
	// 使用IPv6作为底层协议的TCP协议
	asio::ip::tcp protocol = asio::ip::tcp::v6();

	// 步骤 3. 实例化一个接受器socket对象
	asio::ip::tcp::acceptor acceptor(ios);

	// 用于存储打开接受器socket时可能发生的错误信息
	boost::system::error_code ec;

	// 步骤 4. 打开接受器socket
	acceptor.open(protocol, ec);

	if (ec.value() != 0)
	{
		// socket打开失败
		std::cout
			<< "打开接受器socket失败！"
			<< "错误代码 = "
			<< ec.value() << "。消息：" << ec.message();
		return ec.value();
	}

	return 0;
}

int bind_acceptor_socket()
{
	// 步骤 1. 这里我们假设服务器应用程序已经
	// 获取了协议端口号
	unsigned short port_num = 3333;

	// 步骤 2. 创建一个端点
	asio::ip::tcp::endpoint ep(asio::ip::address_v4::any(),
							   port_num);

	// 供'acceptor'类构造函数使用
	asio::io_context ios;

	// 步骤 3. 创建并打开一个接受器socket
	asio::ip::tcp::acceptor acceptor(ios, ep.protocol());

	boost::system::error_code ec;

	// 步骤 4. 绑定接受器socket
	acceptor.bind(ep, ec);

	// 处理可能发生的错误
	if (ec.value() != 0)
	{
		// 接受器socket绑定失败，终止执行
		std::cout << "接受器socket绑定失败。"
				  << "错误代码 = " << ec.value() << "。消息："
				  << ec.message();

		return ec.value();
	}

	return 0;
}

int connect_to_end()
{
	// 步骤 1. 假设客户端应用程序已经获取了
	// 目标服务器的IP地址和协议端口号
	std::string raw_ip_address = "127.0.0.1";
	unsigned short port_num = 3333;

	try
	{
		// 步骤 2. 创建一个端点来指定
		// 目标服务器应用程序
		asio::ip::tcp::endpoint
			ep(asio::ip::address::from_string(raw_ip_address),
			   port_num);

		asio::io_context ios;

		// 步骤 3. 创建并打开一个socket
		asio::ip::tcp::socket sock(ios, ep.protocol());

		// 步骤 4. 连接socket
		sock.connect(ep);

		// 此时socket 'sock'已连接到
		// 服务器应用程序，可以用于
		// 发送数据或从服务器接收数据
	}
	// asio::ip::address::from_string()和
	// asio::ip::tcp::socket::connect()的重载版本
	// 在发生错误时会抛出异常
	catch (system::system_error &e)
	{
		std::cout << "发生错误！错误代码 = " << e.code()
				  << "。消息：" << e.what();

		return e.code().value();
	}
	return 0;
}

int dns_connect_to_end()
{
	// 步骤 1. 假设客户端应用程序已经获取了
	// DNS名称和协议端口号，并将它们表示为字符串
	std::string host = "samplehost.book";
	std::string port_num = "3333";

	// 用于解析器和socket
	asio::io_context ios;

	// 创建解析器查询
	asio::ip::tcp::resolver::query resolver_query(host, port_num,
												  asio::ip::tcp::resolver::query::numeric_service);

	// 创建解析器
	asio::ip::tcp::resolver resolver(ios);

	try
	{
		// 步骤 2. 解析DNS名称
		asio::ip::tcp::resolver::iterator it =
			resolver.resolve(resolver_query);

		// 步骤 3. 创建socket
		asio::ip::tcp::socket sock(ios);

		// 步骤 4. asio::connect()方法会遍历
		// 每个端点，直到成功连接到其中一个
		// 如果无法连接到所有端点或发生其他
		// 错误，将抛出异常
		asio::connect(sock, it);

		// 此时socket 'sock'已连接到
		// 服务器应用程序，可以用于
		// 发送数据或从服务器接收数据
	}
	// asio::ip::tcp::resolver::resolve和
	// asio::connect()的重载版本
	// 在发生错误时会抛出异常
	catch (system::system_error &e)
	{
		std::cout << "发生错误！错误代码 = " << e.code()
				  << "。消息：" << e.what();

		return e.code().value();
	}

	return 0;
}
